﻿#include "Calculator.h"
#include "Regulation.h"
#include "ui_Calculator.h"
#include "INCLUDE/Stack.h"
#include <QKeyEvent>
#include <QMessageBox>

/*******************************************************************************
*类名：MainWindow
*功能：主窗体界面，信号槽连接以及槽函数的实现
*作者：韦锴祖
*日期：2018年12月10号
********************************************************************************/

Calculator::Calculator(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Calculator)
{
    ui->setupUi(this);
    ui->lineEdit_Result->setFocusPolicy(Qt::NoFocus);         //设置光标不可用
    ui->lineEdit_Input->setAlignment(Qt::AlignRight);         //设置显示居右
    ui->lineEdit_Result->setAlignment(Qt::AlignRight);        //设置显示居右
    ui->lineEdit_Input->setFocus();                           //设置光标指向输入框
    m_IsCount = false;                                        //初始化运算未完成

    m_Stack = new Stack;
    connectBtn();                                             //调用按钮连接信号槽函数
    regExp();                                                 //调用限制输入框输入字符函数
}

/**
 * 功能：析构函数
 * 参数：
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
Calculator::~Calculator()
{
    delete ui;
    delete m_Stack;     //释放 m_Stack 的内存空间
}

/**
 * 功能：限制输入框输入的字符
 * 参数：
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::regExp()
{
    QRegExp rx("^(-|\\d|[(])[\\d-.+*/%^!()]+(\\d)$");    //首字符只能输入负号，数字和左括号，字符串只允许输入数字和规定的运算符
    QRegExpValidator *validator = new QRegExpValidator(rx,ui->lineEdit_Input);
    ui->lineEdit_Input->setValidator(validator);
}

/**
 * 功能：按钮信号槽函数
 * 参数：
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::connectBtn()
{
    connect(ui->btnNum0,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnNum1,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnNum2,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnNum3,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnNum4,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnNum5,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnNum6,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnNum7,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnNum8,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnNum9,SIGNAL(clicked(bool)),this,SLOT(inputNumber()));
    connect(ui->btnSub,SIGNAL(clicked(bool)),this,SLOT(inputSpeOperator()));
    connect(ui->btnBack,SIGNAL(clicked(bool)),this,SLOT(inputSpeOperator()));
    connect(ui->btnClear,SIGNAL(clicked(bool)),this,SLOT(inputSpeOperator()));
    connect(ui->btnEqual,SIGNAL(clicked(bool)),this,SLOT(inputSpeOperator()));
    connect(ui->btnDot,SIGNAL(clicked(bool)),this,SLOT(inputOperator()));
    connect(ui->btnAdd,SIGNAL(clicked(bool)),this,SLOT(inputOperator()));
    connect(ui->btnMuti,SIGNAL(clicked(bool)),this,SLOT(inputOperator()));
    connect(ui->btnDivi,SIGNAL(clicked(bool)),this,SLOT(inputOperator()));
    connect(ui->btnLeft,SIGNAL(clicked(bool)),this,SLOT(inputOperator()));
    connect(ui->btnRight,SIGNAL(clicked(bool)),this,SLOT(inputOperator()));
    connect(ui->btnFac,SIGNAL(clicked(bool)),this,SLOT(inputOperator()));
    connect(ui->btnMod,SIGNAL(clicked(bool)),this,SLOT(inputOperator()));
    connect(ui->btnPow,SIGNAL(clicked(bool)),this,SLOT(inputOperator()));
    //connect(ui->at_aboutHelp,SIGNAL(triggered(bool)),this,SLOT(Help()));
}

/**
 * 功能：点击按钮输入数字实现
 * 参数：numButton 定义的按钮
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::inputNumber()
{
    QPushButton *numButton = qobject_cast<QPushButton *>(this->sender());  //定义一个按钮事件，并指定此事件为信号发送者
    if(numButton)                                                          //如果点击了数字按钮，就会调用按钮输入数字函数
    {
        QString btnText = numButton->text();
        setNumber(btnText);
    }
}

/**
 * 功能：点击按钮输入运算符实现
 * 参数：opButton 定义的按钮
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::inputOperator()
{
    QPushButton *opButton = qobject_cast<QPushButton *>(this->sender());  //定义一个按钮事件，并指定此事件为信号发送者
    if(opButton)
    {
        QString btnText = opButton->text();
        setOperator(btnText);
    }
}

/**
 * 功能：点击按钮输入特殊运算符实现
 * 参数：opButton 定义的按钮
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::inputSpeOperator()
{
    QPushButton *opButton = qobject_cast<QPushButton *>(this->sender());  //定义一个按钮事件，并指定此事件为信号发送者
    if(opButton)                                                          //如果点击了运算符按钮，就会调用按钮输入运算符函数
    {
        if(opButton == ui->btnSub)
        {
            setSub("-");
        }
        if(opButton == ui->btnBack)
        {
            Back();
        }
        if(opButton == ui->btnClear)
        {
            allClear();
        }
        if(opButton == ui->btnEqual)
        {
            Equal();
        }
    }
}

/**
 * 功能：点击键盘Enter触发"="按钮
 * 参数：无
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
    {
        Equal();
    }
}

/**
 * 功能：把数字输入到输入框
 * 参数：str 输入的数字
 *      m_IsCount 判断计算是否完成
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::setNumber(QString str)
{
    QString s = ui->lineEdit_Input->text();         //获取输入框的字符串
    if(!m_IsCount)                                  //如果运算没有完成，则在之前从lineEdit获取到的字符串后面加上输入的数字
        ui->lineEdit_Input->setText(s+str);
    else
    {
        ui->lineEdit_Input->setText(str);           //如果运算已经完成，则直接在lineEdit中输入的数字，清空结果并设置表达式为未完成运算
        ui->lineEdit_Result->clear();
        m_IsCount = false;
    }
    ui->lineEdit_Input->setFocus();                 //设置光标指向输入框
}

/**
 * 功能：把运算符输入到输入框
 * 参数：str 输入的运算符
 *      m_IsCount 判断计算是否完成
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::setOperator(QString str)
{
    QString s = ui->lineEdit_Input->text();         //获取输入框的字符
    QString s1 = ui->lineEdit_Result->text();       //获取结果框的字符
    if(s==NULL && !(str=="(" || str =="-"))         //限制表达式首字符只允许输入负号和左括号
    {
        QMessageBox::warning(this,tr("错误!"),tr("首字符只允许数字、左括号和负号"));
        return;
    }
    if(!m_IsCount)                                   //如果运算没有完成，则在之前从lineEdit获取到的字符串后面加上输入的运算符
    {
        ui->lineEdit_Input->setText(s+str);
    }
    else                                            //如果运算已经完成，则获取计算结果，在计算结果后面输入运算符，清空结果并设置表达式为未完成运算
    {
        ui->lineEdit_Input->setText(s1+str);
        ui->lineEdit_Result->clear();
        m_IsCount = false;
    }
    ui->lineEdit_Input->setFocus();                 //设置光标指向输入框
}

/**
 * 功能：减号做特殊处理，实现负数运算
 * 参数：str 输入的减号
 *      m_IsCount 判断计算是否完成
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::setSub(QString str)
{
    QString s = ui->lineEdit_Input->text();
    if(!m_IsCount)
    {
        ui->lineEdit_Input->setText(s+str);
    }
    else
    {
        ui->lineEdit_Input->setText(ui->lineEdit_Result->text()+str);
        ui->lineEdit_Result->clear();
        m_IsCount = false;
    }
    ui->lineEdit_Input->setFocus();
}


/**
 * 功能：退格
 * 参数：
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::Back()
{
    QString s = ui->lineEdit_Input->text();         //获取输入的字符串
    s=s.mid(0,s.size()-1);                          //使用截取函数，把从第 1 位开始到比字符串长度小 1 的字符串截取出来
    ui->lineEdit_Input->setText(s);                 //把截取出来的字符串放到输入框上
    m_IsCount = false;
    ui->lineEdit_Input->setFocus();
}

/**
 * 功能：清空
 * 参数：
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::allClear()                         //调用Stack类的接口，初始化计算器，并清空输入框，结果框，状态栏上的数据
{
    ui->statusBar->showMessage("");
    ui->lineEdit_Input->clear();
    ui->lineEdit_Result->clear();
    ui->lineEdit_Input->setFocus();
}

/**
 * 功能：点击等号进行运算
 * 参数：m_Stack Stack类的一个对象
 *      reg Regulation类的一个对象
 *      m_IsCount 判断计算是否完成
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
void Calculator::Equal()
{
    Regulation *reg = new Regulation;                                 //调用Regulation和Stack类的接口
    statusBar()->showMessage("");                                     //初始化状态栏信息为空
    if(ui->lineEdit_Input->text()==NULL)                              //如果还没输入，那么等于号不起作用
    {
        ui->lineEdit_Result->clear();
        return;
    }
    else if(reg->IsRegulation(ui->lineEdit_Input->text()))            //表达式先调用规则函数进行合法性判断，如果合法则进入循环计算
    {
        QString str = ui->lineEdit_Input->text();                     //获取输入的表达式
        if(str.at(0)=='-' && str.at(1)=='(')                          //如果第一个字符是"-"并且第二个字符为"(" 则在首字符加入"0"
        {
           QString s="0"+str;
           m_Stack->setPostfix(s.toStdString());                                    //调用toPostfix()函数进行中缀转后缀表达式
        }
        else
        {
          m_Stack->setPostfix(str.toStdString());
        }
        m_Stack->setCalResults();                                        //计算结果
        ui->lineEdit_Result->setText(QString::number(m_Stack->getResult()));           //把结果放到结果框中
        statusBar()->showMessage("后缀表达式："+QString::fromStdString(m_Stack->getPostfix())); //把后缀表达式在状态栏中显示出来
        m_IsCount = true;                                             //初始化表达式已计算完成
    }
    ui->lineEdit_Input->setFocus();
    m_Stack->clearPostfix();                                          //清空后缀表达式容器
    delete reg;                                                       //释放reg和sta1空间
}

/**
 * 功能：跳转帮助界面
 * 参数：
 * 作者：韦锴祖
 * 日期：2018-12-8
 **/
//void Calculator::Help()
//{
//    QAction *action = qobject_cast<QAction *>(this->sender());        //定义一个按钮事件，并指定此事件为信号发送者
//    if(action == ui->at_aboutHelp)
//    {
//        aboutHelp *ad =  new aboutHelp;
//        ad->setWindowTitle("关于计算器");
//        ad->setWindowIcon(QIcon(":Image/Image/icon.png"));
//        ad->show();
//    }
//}
